<template>
  <div>
    <el-button @click="beginMappingSetting" size="small"
      >{{ recovery ? '回收映射' : '分发映射' }}配置</el-button
    >
    <el-dialog
      :title="recovery ? '回收映射' : '分发映射'"
      width="800px"
      append-to-body
      class="mapping-dialog"
      destroy-on-close
      :visible.sync="dialogVisible"
      :before-close="handleClose"
    >
      <mapping-panel
        ref="mappingPanel"
        :trigger-type="triggerType"
        :creator-follow-data-key="creatorFollowDataKey"
      />
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" size="small" @click="handleDialogSure">
          确 定
        </el-button>
        <el-button size="small" @click="handleClose">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import req from '@/request.js'
import MappingPanel from './MappingPanel.vue'

const Base64 = require('js-base64').Base64

export default {
  name: 'MappingDialog',
  components: {MappingPanel},
  props: {
    value: {
      type: String
    },
    // 是否是回收映射
    recovery: {
      type: Boolean,
      default: false
    },
    defId: {
      type: String,
      required: true
    },
    nodeId: {
      type: String,
      required: true
    },
    triggerType: {
      type: String,
      required: true
    },
    creatorFollowDataKey: String
  },
  data() {
    return {
      dialogVisible: false,
      varTree: null,
      subflowVarTree: null
    }
  },
  watch: {
    dialogVisible: function(newVal, oldVal) {
      if (newVal !== oldVal && newVal) {
        this.loadVarTree()
        this.loadSubFlowVarTree()
      }
    }
  },
  methods: {
    loadVarTree() {
      this.varTree = null
      this.getVarTreeWithDefId(this.defId)
        .then(data => {
          this.varTree = data
          this.extractNodesFromVarTree()
        })
        .catch(err => {
          this.$message.error(
            '未获取到主流程绑定的业务模型，无法进行属性映射配置。'
          )
          throw err
        })
    },
    loadSubFlowVarTree() {
      this.subflowVarTree = null
      // 通过当前节点获取绑定的子流程defId
      req
        .get(
          `${window.context.bpmModel}/flow/def/v1/subFlowDetail?defId=${this.defId}&nodeId=${this.nodeId}`
        )
        .then(response => {
          const {data} = response
          return data.defId
        })
        .then(subflowDefId => {
          // 通过子流程defId获取表单业务对象
          return this.getVarTreeWithDefId(subflowDefId)
        })
        .then(data => {
          this.subflowVarTree = data
          this.extractNodesFromVarTree()
        })
        .catch(err => {
          this.$message.error(
            '未获取到子流程绑定的业务模型，无法进行属性映射配置。'
          )
          throw err
        })
    },
    // 通过defId获取varTree
    getVarTreeWithDefId(defId) {
      return new Promise((resolve, reject) => {
        req
          .post(`${window.context.bpmModel}/flow/node/v1/varTree`, {
            defId,
            includeBpmConstants: false,
            removeSub: false,
            bpmForm: true
          })
          .then(resp => {
            let result = null
            const {data} = resp
            if (data && data.length > 0) {
              data.forEach(d => {
                if (d.id === '0') {
                  result = d
                }
              })
            }
            if (result) {
              resolve(result)
            } else {
              reject()
            }
          })
      })
    },
    extractNodesFromVarTree() {
      if (this.varTree && this.subflowVarTree) {
        let originNodes = []
        // 构建来源节点数组(回收映射时以子流程业务对象为来源)
        this.buildNode(
          originNodes,
          this.varTree.children,
          this.recovery ? 'RS' : 'Origin',
          'main'
        )
        originNodes = this.filterNodesByTriggerType(
          originNodes,
          this.recovery ? 'RS' : 'Origin'
        )
        // 根据每个节点中列的数量计算top值
        this.calcTopValueOfNodes(originNodes)
        let rsNodes = []
        // 构建目标节点数组(回收映射时以主流程业务对象为目标)
        this.buildNode(
          rsNodes,
          this.subflowVarTree.children,
          this.recovery ? 'Origin' : 'RS',
          'sub'
        )
        rsNodes = this.filterNodesByTriggerType(
          rsNodes,
          this.recovery ? 'Origin' : 'RS'
        )
        // 根据每个节点中列的数量计算top值
        this.calcTopValueOfNodes(rsNodes)
        const nodes = originNodes.concat(rsNodes)
        let edges = null
        try {
          if (this.value) {
            edges = JSON.parse(Base64.decode(this.value))
          }
        } catch (err) {}
        this.$refs.mappingPanel.renderWithJson({nodes, edges})
      }
    },
    // 根据子流程触发类型进行过滤
    // 触发类型为：多人发起不同数据/一人发起不同数据，主流程只显示子表实体，子流程只显示主表实体
    filterNodesByTriggerType(nodes, type) {
      if (!this.recovery && this.triggerType != 'multiSameData') {
        const filterType = type == 'RS' ? 'main' : 'sub'
        return nodes.filter(n => n.nodeType == filterType)
      }
      return nodes
    },
    // 同一种类型的节点 之间设置不同的top，避免图形叠合在一起
    calcTopValueOfNodes(nodes) {
      nodes.forEach((n, index) => {
        const colCount = this.getColumnCountByType(
          nodes.slice(index + 1, nodes.length)
        )
        n.top = 35 * (colCount + 1)
      })
    },
    // 获取节点数组中columns的数量
    getColumnCountByType(nodes) {
      return nodes.reduce((pre, n) => {
        return (pre += n.columns.length)
      }, 0)
    },
    // 构建节点
    buildNode(nodes, list, type, flowType) {
      list.forEach(obj => {
        const {name, comment, children, nodeType} = obj

        const node = {
          id: `${type}_${name}`,
          name,
          comment,
          type,
          nodeType,
          flowType,
          top: 20,
          left: type == 'RS' ? 310 : 10
        }

        if (children && children.length > 0) {
          node.columns = this.buildColumn(
            nodes,
            children,
            type,
            nodeType,
            flowType
          )
        }
        nodes.push(node)
      })
      return nodes
    },
    // 构建节点中的列
    buildColumn(nodes, children, type, parentNodeType, flowType) {
      const columns = []
      children.forEach(obj => {
        const {name, comment, nodeType} = obj
        if (nodeType == 'field') {
          const colum = {
            name,
            comment
          }
          columns.push(colum)
        } else if (parentNodeType == 'main' && nodeType == 'sub') {
          this.buildNode(nodes, [obj], type, flowType)
        }
      })
      return columns
    },
    beginMappingSetting() {
      this.dialogVisible = true
    },
    handleDialogSure() {
      const edgeData = this.$refs.mappingPanel.getEdgeData()
      let mappingData = null
      if (edgeData && edgeData.length > 0) {
        mappingData = JSON.stringify(edgeData)
        mappingData = Base64.encode(mappingData)
      }
      const creatorFollowDataKeyResult = this.$refs.mappingPanel.buildCreatorFollowDataKey()
      if (creatorFollowDataKeyResult) {
        this.$emit('update:creatorFollowDataKey', creatorFollowDataKeyResult)
      }
      this.$emit('input', mappingData)
      this.dialogVisible = false
    },
    handleClose() {
      this.dialogVisible = false
    }
  }
}
</script>
<style lang="scss" scoped>
.mapping-dialog {
  ::v-deep {
    .el-dialog__header {
      padding: 10px;
      border-bottom: 1px solid #ededed;
      button {
        top: 14px;
      }
    }
    .el-dialog__body {
      padding: 10px 20px;
    }
  }
}
</style>
